#include <chrono>
#include <future>
#include <vector>
#include <iostream>
#include <atomic>
#include <mutex>

class Singleton
{
public:
    static Singleton &get_instance()
    {
        Singleton *ins = _instance.load(std::memory_order_acquire);
        if (!ins)
        {
            std::lock_guard lk(_m);
            if (!ins)
            {
                ins = new Singleton();
                _instance.store(ins, std::memory_order_release);
            }
        }
        volatile int t{};
        return *ins;
    }

private:
    Singleton() = default;
    ~Singleton() = default;
    Singleton(const Singleton &) = delete;
    Singleton(Singleton &&) = delete;
    Singleton &operator=(const Singleton &) = delete;
    Singleton &operator=(Singleton &&) = delete;
    static std::atomic<Singleton *> _instance;
    static std::mutex _m;
};

// initialization of static variables
std::atomic<Singleton *> Singleton::_instance;
std::mutex Singleton::_m;

int main(int argc, char **arg)
{
    if (argc != 2)
        return 0;
    int thread_number = atoi(arg[1]);
    auto f = []() -> long long int {
        auto start = std::chrono::high_resolution_clock::now();
        for (size_t i = (size_t)0; i < 100'000'000; ++i)
            Singleton::get_instance();
        auto end = std::chrono::high_resolution_clock::now();
        return std::chrono::duration_cast<std::chrono::nanoseconds>(end - start).count();
    };
    std::vector<std::future<long long int>> fts;
    fts.reserve(thread_number);
    for (int i = 0; i < thread_number; ++i)
        fts.emplace_back(std::async(std::launch::async, f));
    long long int total_time = 0;
    for (auto &&ft : fts)
    {
        total_time += ft.get();
    }
    std::cout << total_time / (thread_number) / 1000000 << ", ";
    return 0;
}